package top.eggcode.plugins.session.manager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.eggcode.plugins.session.ProxySession;
import top.eggcode.plugins.session.Session;
import top.eggcode.plugins.session.exception.InvalidSessionException;
import top.eggcode.plugins.session.exception.SessionException;
import top.eggcode.plugins.session.exception.UnknownSessionException;

import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;

/**
 * Title: 本机会话管理器
 * Description: 管理所有的应用，包括session的创建、持久化、删除
 * Date: 2021/6/12 19:47
 *
 * @author JiaQi Ding
 * @version 1.0
 */
public abstract class NativeSessionManager implements SessionManager {

    private static final Logger log = LoggerFactory.getLogger(NativeSessionManager.class);


    @Override
    public Session start() {
        Session session = createSession();
        return createExposedSession(session, (String) session.getId());
    }

    ;

    /**
     * 创建全局会话，set get操作会同步至数据库
     *
     * @param session
     * @param key
     * @return
     */
    protected Session createExposedSession(Session session, Serializable key) {
        return new ProxySession(this, session.getId());
    }

    private Session lookupRequiredSession(Serializable key) {
        Session session = lookupSession(key);
        if (session == null) {
            String msg = "Unable to locate required Session instance based on SessionKey [" + key + "].";
            throw new UnknownSessionException(msg);
        }
        return session;
    }

    private Session lookupSession(Serializable key) throws SessionException {
        if (key == null) {
            throw new NullPointerException("SessionKey argument cannot be null.");
        }
        return doGetSession(key);
    }

    protected abstract Session createSession();

    @Override
    public Session getSession(Serializable key) {
        Session session = lookupSession(key);
        return session != null ? createExposedSession(session, key) : null;
    }

    @Override
    public Date getStartTimestamp(Serializable key) {
        return lookupRequiredSession(key).getStartTimestamp();
    }

    @Override
    public Date getLastAccessTime(Serializable key) {
        return lookupRequiredSession(key).getLastAccessTime();
    }

    @Override
    public boolean isValid(Serializable key) {
        try {
            checkValid(key);
            return true;
        } catch (InvalidSessionException e) {
            return false;
        }
    }

    @Override
    public void checkValid(Serializable key) throws InvalidSessionException {
        lookupRequiredSession(key);
    }

    @Override
    public long getTimeout(Serializable key) throws InvalidSessionException {
        return lookupRequiredSession(key).getTimeout();
    }

    @Override
    public void setTimeout(Serializable key, long maxIdleTimeInMillis) throws InvalidSessionException {
        Session s = lookupRequiredSession(key);
        s.setTimeout(maxIdleTimeInMillis);
        onChange(s);
    }

    @Override
    public void touch(Serializable key) throws InvalidSessionException {
        Session s = lookupRequiredSession(key);
        s.touch();
        onChange(s);
    }

    @Override
    public String getHost(Serializable key) {
        return lookupRequiredSession(key).getHost();
    }

    @Override
    public void stop(Serializable key) throws InvalidSessionException {
        Session session = lookupRequiredSession(key);
        try {
            if (log.isDebugEnabled()) {
                log.debug("Stopping session with id [" + session.getId() + "]");
            }
            session.stop();
            onStop(session, key);
            //notifyStop(session);
        } finally {
            //afterStopped(session);
        }
    }

    @Override
    public Collection<Object> getAttributeKeys(Serializable key) {
        Collection<Object> c = lookupRequiredSession(key).getAttributeKeys();
        if (!(c == null || c.isEmpty())) {
            return Collections.unmodifiableCollection(c);
        }
        return Collections.emptySet();
    }

    @Override
    public Object getAttribute(Serializable sessionKey, Object attributeKey) throws InvalidSessionException {
        return lookupRequiredSession(sessionKey).getAttribute(attributeKey);
    }

    @Override
    public void setAttribute(Serializable sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
        if (value == null) {
            removeAttribute(sessionKey, attributeKey);
        } else {
            Session s = lookupRequiredSession(sessionKey);
            s.setAttribute(attributeKey, value);
            onChange(s);
        }
    }

    @Override
    public Object removeAttribute(Serializable sessionKey, Object attributeKey) throws InvalidSessionException {
        Session s = lookupRequiredSession(sessionKey);
        Object removed = s.removeAttribute(attributeKey);
        if (removed != null) {
            onChange(s);
        }
        return removed;
    }


    protected abstract Session doGetSession(Serializable sessionKey) throws InvalidSessionException;


    protected void onChange(Session s) {
    }

    protected void onStop(Session session) {
        onChange(session);
    }

    protected void onStop(Session session, Serializable key) {
        onStop(session);
    }

}
